<?php

/**
 * 全局公用函数
 *
 * @package lib
 * @author  suning <snsnsky@gmail.com>
 *
 * $Id$
 */

// 自动加载
spl_autoload_register(function ($class) {
    static $class_map = [];
    if (!isset($class_map[$class])) {
        $class = strtr($class, '\\', DIRECTORY_SEPARATOR);

        $class_file = ord($class[0]) > 96 ? BASEPATH_FRAMEWORK : BASEPATH_MODULE;
        $class_file .= $class . '.class.php';

        if (is_file($class_file)) {
            include_once($class_file);
        }
        // 无论成功失败, 自动加载只进行一次
        $class_map[$class] = $class;
    }
});

spl_autoload_register(function ($class) {
    if (strpos($class, 'MongoDB') !== false) {
        $class = strtr($class, '\\', DIRECTORY_SEPARATOR);
        $class_file = BASEPATH_LIBRARY . $class . '.php';
        if (is_file($class_file)) {
            include_once($class_file);
        }
    }
    if (strpos($class, 'OSS') !== false) {
        $class = strtr($class, '\\', DIRECTORY_SEPARATOR);
        $class_file = BASEPATH_LIBRARY . $class . '.php';
        if (is_file($class_file)) {
            include_once($class_file);
        }
    }
});


/**
 * 全局错误记录
 *
 */
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
    static $_error_types = [
        E_ERROR => 'E_RROR',
        E_WARNING => 'E_WARNING',
        E_STRICT => 'E_STRICT',
        E_USER_ERROR => 'E_USER_ERROR',
        E_USER_WARNING => 'E_USER_WARNING',
        E_USER_NOTICE => 'E_USER_NOTICE'
    ];
    static $_user_error_map = [
        E_ERROR => E_USER_ERROR,
        E_WARNING => E_USER_WARNING,
        E_STRICT => E_USER_NOTICE
    ];

    ob_start();
    debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
    $backtrace = ob_get_contents();
    ob_end_clean();

    $errtype = $_error_types[$errno];
    $info = [
        'URL: ' . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''),
        'REF: ' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''),
        'DATE: ' . date('Y-m-d H:i:s'),
        'INFO: ' . "错误级别: {$errtype}({$errno}), 文件: {$errfile}, 行号: {$errline}",
        'MSG: ' . $errstr,
        'TRACE: ' . $backtrace,
    ];

    $log = new \lib\plugins\Log('Debug/E_USER_NOTICE', $info, PHP_EOL);
    $log->write();

    trigger_error("{$errstr} {$errtype}({$errno}) in {$errfile} on line {$errline}",
        (isset($_user_error_map[$errno]) ? $_user_error_map[$errno] : $errno));
},
    (APP_REALTIME_LOG || __ENV__ !== 'ONLINE') ? (E_ERROR | E_WARNING | E_STRICT | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE) : E_USER_NOTICE);

/**
 * 显示404错误和页面, 并退出
 */
function show_404()
{
    header('HTTP/1.0 404 Not Found');
    header('Content-Type: text/html; charset=UTF-8');
    readfile(ROOT_PATH . 'public/error/404.html');
    exit;
}

/**
 * 分表hash计算
 *
 * @param integer $id 项目ID
 * @return string
 */
function table($id)
{
    return sprintf('%02x', intval($id) % 256);
}

/**
 * 对Smarty中的html标记添加引号的过滤
 *
 * @param String $str
 * @return String
 */
function _htmlspecialchars($str)
{
    return htmlspecialchars($str, ENT_QUOTES);
}

/**
 * 加密ID
 *
 * @param int $id 产品ID(必须为正数)
 * @return string 加密后的ID

function encode_id(int $id): string
{
    $salt = sprintf("%02u", $id % 97);

    return base_convert($id . $salt, 10, 36);
}

/**
 * 解密ID
 *
 * @param string $id 加密后的产品ID
 * @return int 解密后的ID

function decode_id(string $id): int
{
    $number = base_convert($id, 36, 10);

    return substr($number, 0, strlen($number) - 2);
}
*/


/**
 * 根据用户ID 获取头像
 *
 * @param integer $userid 用户ID
 * @param integer $size   头像尺寸 180/100
 * @param integer $ver    头像版本, >1 才体现
 * @param bool    $isbaby 是否是宝宝头像, 为true返回宝宝头像地址
 * @return string         返回头像地址
 */
function head($userid, $size = 100, $ver = 0)
{
    static $allow_size = ['180' => 1, '100' => 1];

    $hash1 = sprintf("%02x", $userid % 256);
    $hash2 = sprintf("%02x", $userid / 256 % 256);
    $size = isset($allow_size[$size]) ? $size : '100';
    $ver = $ver > 0 ? '?v=' . $ver : '';

    return autohost("http://img." . CURRENT_HOST . "/user/head/{$hash1}/{$hash2}/{$userid}_{$size}.jpg{$ver}");
}

/**
 * 清除CDN缓存
 *
 * @param string|array $urls url地址,支持数组
 * @return boolean
 */
function cdn_cache_purge($urls)
{
    $urls = is_array($urls) ? $urls : [$urls];

    return \Queue\Queue::addMulti("CDNCachePurge", $urls);
}

/**
 * 读取配置文件
 *
 * @param string $path 路径, 如 mobileapi/base
 * @param string $ext  扩展名, 默认.cfg.php
 * @return array       不存在时返回空数组
 */
function loadconf($path, $ext = '.cfg.php')
{
    static $confs = [];
    $name = $path . $ext;
    $path = BASEPATH_CONFIG . $name;
    $name .= SITE_SUFFIX;
    if (!$confs[$name]) {
        $ta = false;
        if (!empty(SITE_SUFFIX)) { // 优先加载带后缀配置
            $tpath = str_replace($ext, SITE_SUFFIX . $ext, $path);
            if (is_file($tpath)) {
                $ta = true;
                $conf = include($tpath);
                $confs[$name] = $conf;
            }
        }
        if (is_file($path) && !$ta) {
            $conf = include($path);
            $confs[$name] = $conf;
        }
    }

    return is_array($confs[$name]) ? $confs[$name] : [];
}

/**
 * 根据给定时间参数与当前时间的差值转化时间格式
 *
 *    今天内的时间将格式化为:
 *       刚刚  N分钟前 / N小时前
 *    1 年内的时间将格式化为:
 *       MM-DD
 *    1 年前的时间将格式化为:
 *       YYYY-MM-DD
 *
 * @param string  $datetime     日期时间字符串,支持的格式请参考strtotime
 * @param boolean $is_timestamp 当$datetime为时间戳时设为true
 * @return string
 */
function date_custom($datetime, $is_timestamp = false)
{
    static $OFFSET = 28800; // 8 小时时差
    static $ONEDAY = 86400; // 1 天
    static $REMAINS = 57599; // 86400 - 28800 - 1 秒

    $dtformat = 'm-d';

    // 当前时间
    $now = $_SERVER['REQUEST_TIME'];
    $seconds = $now % $ONEDAY;
    $midnight = $now - $seconds + $REMAINS;
    // 给定日期
    $timestamp = (is_numeric($datetime) || $is_timestamp) ? $datetime : strtotime($datetime);
    // 与今日最后一秒时间差
    $xdiff = $midnight - $timestamp;
    // 天数差
    $days = intval($xdiff / $ONEDAY);
    // 与当前时间的差值
    $diff = $now - $timestamp;
    // 补上时差
    $timestamp += $OFFSET;
    if ($days == 0) {
        if ($diff > 3600) {
            return intval($diff / 3600) . '小时前';
        } elseif ($diff > 60) {
            return intval($diff / 60) . '分钟前';
        } else {
            return '刚刚';
        }
    } else {
        $this_year = (int)date('Y', $now);
        $that_year = (int)date('Y', $timestamp);
        if ($this_year !== $that_year) {
            $dtformat = 'Y-m-d';
        }
    }

    return gmdate($dtformat, $timestamp);
}

/**
 * 输出带PRE的调试信息
 *
 * @param mixed $extra 多个变量
 * @return void
 */
function pre(...$extra)
{
    $args = func_get_args();
    echo "<pre>\n";
    foreach ($args as $v) {
        print_r($v);
        echo "\n";
    }
    echo "</pre>\n";
}

/**
 * 通过FriePHP进行调试，将调试信息输出到头消息中
 *
 * @return \lib\core\FirePHP|bool
 */
function fb()
{
    if (defined('DEBUG') && DEBUG) {

        static $firephp = null;
        if (!$firephp) {
            $firephp = \lib\core\FirePHP::getInstance(true);
        }
        $firephp->fb(func_get_args());

        return $firephp;
    }

    return false;
}


/**
 * 记录日志
 *
 * @param string       $path    保存路径和文件名前缀，必选。如"a/b/c", "a/b"为目录， "c"为文件名前缀
 * @param string|array $data    需要保存的数据，必选。如"abcdefg"或array('a','b','c','d')
 * @param string       $title   自定义日志文件名部分，非必选。默认为空，如果设置则文件名跟着变化，如设置d，文件名则是：c_d_20120315.log或c_d.log
 * @param boolean      $logtime 设置文件名是否需要按时间命名。默认true, 文件名格式：c_20120315.log, 可设置false，文件名格式：c.log
 * @param string       $sep     设置数据的分隔符，默认为\t
 * @return boolean              返回记录日志是否成功。 true|false
 */
function runtime_log($path, $data, $title = "", $logtime = true, $sep = "\t")
{
    if (!$path || !$data) {
        return false;
    }

    global $app;

    $logger = $app->log();
    $logger->setPath($path);

    if ($title) {
        $logger->setTitle($title);
    }
    if ($logtime === false) {
        $logger->setNoTime();
    }
    if ($sep != "\t") {
        $logger->setSep($sep);
    }

    $logger->resetData();
    $logger->setData($data);
    $flag = $logger->write();

    return $flag;
}

/**
 * 加密 Cookie
 *
 * @param mixed $data 待加密的数据
 * @return string     返回一个包含初始向量(IV)、加密数据的字符串，均以十六进制表示，前 16 位为IV
 */
function cookie_encode($data)
{
    $checksum = sprintf("%u", crc32($_SERVER['HTTP_USER_AGENT']));
    $serialized = $checksum . '|' . json_encode($data);

    return \Encrypt\Blowfish::encode($serialized);

}

/**
 * 解密 Cookie
 *
 * @param string $encoded_hex 已加密数据
 * @return mixed              原始数据
 */
function cookie_decode($encoded_hex)
{
    $encoded_hex = trim($encoded_hex);
    $serialized = \Encrypt\Blowfish::decode($encoded_hex);
    if ($serialized === false) {
        return false;
    }
    $arr = explode('|', $serialized);
    if (count($arr) != 2) {
        return false;
    }
    $checksum = sprintf("%u", crc32($_SERVER['HTTP_USER_AGENT']));
    if ($checksum != $arr[0]) {
        return false;
    }

    return json_decode($arr[1], true);
}

/**
 * 手工拼接SQL时, 内容转义 (已经连接PDO时, 推荐 PDO::quote)
 *
 * @param string $str            要转义的字符串
 * @param bool   $utf8_safe      是否使用UTF8编码检查, 默认true(开启)
 * @param bool   $filter_percent 是否转义%, 默认false(关)
 * @return mixed
 */
function mysql_quote($str, $utf8_safe = true, $filter_percent = false)
{
    if (!is_string($str)) {
        return '';
    }

    if ($utf8_safe) {
        $str = iconv("UTF-8//IGNORE", "UTF-8//IGNORE", $str);
    }

    $str = addslashes($str);

    $search = ["\r", "\n"];
    $replace = ['\r', '\n'];

    if ($filter_percent) {
        $search[] = '%';
        $replace[] = '\%';
    }

    return str_replace($search, $replace, $str);
}

/**
 *  返回距离 1970年01月01日00时00分00秒
 *
 * @param $timestamp
 * @return int
 */
function time2Day($timestamp)
{
    //距离 2001-01-01 00:00:00
    return (int)(($timestamp - 978278400) / 86400);
}

function day2timestamp($day)
{
    return $day * 86400 + 978278400;
}

/**
 * 连续签到天数
 *
 * @param array $signRecord 数组为 time2Date 返回的值
 * @param  int  $today      今天，time2Date 返回的值
 * @return int
 */
function getContinuousSignDays(&$signRecord, $today)
{
    $c = 0;
    $count = count($signRecord);
    if ($count == 0) {
        return $c;
    }
    $count--;
    $today--;
    while ($count >= 0 && $signRecord[$count] == $today) {
        $count--;
        $today--;
        $c++;
    }

    return $c;
}

/**
 * 计算年龄
 *
 * @param $nowDate
 * @param $birthday
 * @return mixed
 */
function getAge($nowDate, $birthday)
{
    list($by, $bm, $bd) = explode('-', $birthday);
    list($cy, $cm, $cd) = explode('-', $nowDate);

    $age = $cy - $by - 1;
    if ($cm > $bm || $cm == $bm && $cd > $bd) {
        $age++;
    }

    return $age;
}

/**
 * 获取日期 2017-10-01
 *
 * @param $datetime
 * @return mixed
 */
function getDateByDateTime($datetime)
{
    return explode($datetime, ' ')[0];
}

/**
 * @param $str
 * @param $data
 * @return mixed
 */
function convertInputExp($str, $data)
{
    if (empty($str) || !is_array($data)) {
        return $str;
    }
    $fields = [
        'EndTkRate',//佣金
        'Discount',//折扣
        'ItemType',//商品类型
        'ZkFinalPrice',//现价
        'CouponAmt'//佣金
    ];

    foreach ($fields as $val) {
        if (isset($data[$val])) {
            $str = str_replace($val, $data[$val], $str);
        }
    }

    return $str;
}

/**
 * 格式化价格 保留2位小数
 *
 * @param $str
 * @return string
 */
function formatPrice($str)
{
    return sprintf("%.2f", substr(sprintf("%.3f", $str), 0, -1));
}

/**
 * 格式化商品价格
 *
 * @param $price
 * @return int
 */
function formatItemPrice($price)
{
    return sprintf("%.2f", substr(sprintf("%.3f", $price / 100), 0, -1));
}

/**
 * 解析商品价格
 *
 * @param $price
 * @return int
 */
function parseItemPrice($price)
{
    return (int)($price * 100);
}


/**
 * 获取|保存 文件路径
 *
 * @param string    $path   路径
 * @param int       $itemid 项目id
 * @param string    $ext    图片保存扩展名
 * @param int|array $size   图片裁剪大小
 * @return mixed|string
 */
function getSavePath($path, $itemid = 0, $ext = 'jpg', $size = 0)
{
//    if (IS_ONLINE) {
        $base_dir = '/data/pic/';
        $img_host = 'http://img.jinguonet.com/';
//    } else {
//        $base_dir = BASEPATH_PUBLIC . 'temp/';
//        $img_host = 'http://img.jinguonet.com/';
//    }
    if ($itemid) {
        $hash1 = sprintf("%02x", $itemid % 256);
        $hash2 = sprintf("%02x", $itemid / 256 % 256);
        $name = $itemid;
    } else {
        $time = time();
        $hash1 = date('Ym', $time);
        $hash2 = date('d', $time);
        $name = mt_rand(10, 99) . '_' . substr(md5(__KEY__ . uniqid()), -6);
    }
    if (is_array($size)) {
        $return = [];
        foreach ($size as $v) {
            $v = intval($v);
            $url = $img_host . "{$path}/{$hash1}/{$hash2}/{$name}_{$v}.{$ext}";
            $return[$v] = [
                'url' => $url,
                'path' => $base_dir . "{$path}/{$hash1}/{$hash2}/{$name}_{$size}.{$ext}"
            ];
        }
    } else {
        $size = intval($size);
        $url = $img_host . "{$path}/{$hash1}/{$hash2}/{$name}_{$size}.{$ext}";
        $return = [
            'url' => $url,
            'path' => $base_dir . "{$path}/{$hash1}/{$hash2}/{$name}_{$size}.{$ext}"
        ];
    }

    return $return;
}